library(tidyverse)
── Attaching core tidyverse packages ──────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     ── Conflicts ────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(WGCNA)
Loading required package: dynamicTreeCut
Loading required package: fastcluster

Attaching package: ‘fastcluster’

The following object is masked from ‘package:stats’:

    hclust


Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'rmarkdown':
  method         from
  print.paged_df     
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

Attaching package: ‘WGCNA’

The following object is masked from ‘package:stats’:

    cor
library(gplots)

Attaching package: ‘gplots’

The following object is masked from ‘package:stats’:

    lowess
options(stringsAsFactors = FALSE)

load sample info

sample.description <- read.csv("../input/sample.description.csv")

load reads

lcpm <- read.csv("../output/log2cpm.csv.gz", row.names = 1, check.names = FALSE)
head(lcpm)
dim(lcpm)
[1] 26704    48

Filter for sporophyte samples:

sample.description <- sample.description %>% filter(str_detect(tissue, "S5|WYS"))
lcpm <- lcpm[,sample.description$sample]

Filter for genes with the highest coefficient of variation

CV <- apply(lcpm, 1, \(x) abs(sd(x)/mean(x)))
hist(log10(CV))

names(CV) <- rownames(lcpm)
CV[str_detect(names(CV), "18G076300|33G031700")]
Ceric.18G076300.v2.1 Ceric.33G031700.v2.1 
          0.08763174           0.18394629 
quantile(CV, 0.23)
       23% 
0.08608846 
lcpm.filter <- lcpm[CV > quantile(CV, 0.23),]
dim(lcpm.filter)
[1] 20562    30

WGCNA wants genes in columns

lcpm.filter.t <- t(lcpm.filter)

Soft thresholding

powers <- c(c(1:10), seq(from = 12, to=20, by=2))
sft <- pickSoftThreshold(lcpm.filter.t, powerVector = powers, verbose = 5,networkType = "signed hybrid", blockSize = 15000)
 pickSoftThreshold: calculating connectivity for given powers...
   ..working on genes 1 through 15000 of 20562
Warning: executing %dopar% sequentially: no parallel backend registered
   ..working on genes 15001 through 20562 of 20562
sizeGrWindow(9, 5)
par(mfrow = c(1,2))
cex1 <- 0.9
# Scale-free topology fit index as a function of the soft-thresholding power
plot(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
     xlab="Soft Threshold (power)",ylab="Scale Free Topology Model Fit,signed R^2",type="n",
     main = paste("Scale independence"))
text(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
     labels=powers,cex=cex1,col="red")
# this line corresponds to using an R^2 cut-off of h
abline(h=0.90,col="red")
# Mean connectivity as a function of the soft-thresholding power
plot(sft$fitIndices[,1], sft$fitIndices[,5],
     xlab="Soft Threshold (power)",ylab="Mean Connectivity", type="n",
     main = paste("Mean connectivity"))
text(sft$fitIndices[,1], sft$fitIndices[,5], labels=powers, cex=cex1,col="red")

choose 8

softPower <- 8
adjacency <- adjacency(lcpm.filter.t, power = softPower, type = "signed hybrid")
# Turn adjacency into topological overlap
TOM <- TOMsimilarity(adjacency, TOMType = "signed");
..connectivity..
..matrix multiplication (system BLAS)..
..normalization..
..done.
dissTOM <- 1-TOM
# Call the hierarchical clustering function
geneTree <- hclust(as.dist(dissTOM), method = "average")
# Plot the resulting clustering tree (dendrogram)
sizeGrWindow(12,9)
plot(geneTree, xlab="", sub="", main = "Gene clustering on TOM-based dissimilarity",
     labels = FALSE, hang = 0.04)

define modules

# We like large modules, so we set the minimum module size relatively high:
minModuleSize <- 30;
# Module identification using dynamic tree cut:
dynamicMods <- cutreeDynamic(dendro = geneTree, distM = dissTOM,
                             deepSplit <- 2, pamRespectsDendro = FALSE,
                             minClusterSize = minModuleSize);
 ..done.
table(dynamicMods)
dynamicMods
   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20 
2597 1248 1139  890  791  778  659  653  508  498  482  459  436  428  421  408  404  380  376  367 
  21   22   23   24   25   26   27   28   29   30   31   32   33   34   35   36   37   38   39   40 
 350  336  314  312  308  306  283  273  251  246  243  236  226  198  198  190  184  174  172  170 
  41   42   43   44   45   46   47   48   49   50   51   52   53   54   55   56 
 145  143  142  121  118  113  110  107   98   94   93   87   87   77   74   61 
# Convert numeric labels into colors
dynamicColors = labels2colors(dynamicMods)
table(dynamicColors)
dynamicColors
        bisque4           black            blue           brown          brown4            cyan 
            110             659            1248            1139             113             428 
      darkgreen        darkgrey     darkmagenta  darkolivegreen      darkorange     darkorange2 
            336             312             198             226             306             118 
        darkred   darkslateblue   darkturquoise     floralwhite           green     greenyellow 
            350             107             314             121             791             482 
         grey60           ivory       lightcyan      lightcyan1      lightgreen      lightpink4 
            404             142             408             143             380              61 
lightsteelblue1     lightyellow         magenta          maroon   mediumpurple3    midnightblue 
            145             376             508              74             170             421 
   navajowhite2          orange      orangered4   paleturquoise  palevioletred3            pink 
             77             308             172             243              87             653 
          plum1           plum2          purple             red       royalblue     saddlebrown 
            174              98             498             778             367             251 
         salmon         salmon4         sienna3         skyblue        skyblue3       steelblue 
            436              87             198             273             184             246 
            tan        thistle1        thistle2       turquoise          violet           white 
            459              93              94            2597             236             283 
         yellow     yellowgreen 
            890             190 
# Plot the dendrogram and colors underneath
sizeGrWindow(8,6)
plotDendroAndColors(geneTree, dynamicColors, "Dynamic Tree Cut",
                    dendroLabels = FALSE, hang = 0.03,
                    addGuide = TRUE, guideHang = 0.05,
                    main = "Gene dendrogram and module colors")

merge similar modules

# Calculate eigengenes
MEList <- moduleEigengenes(lcpm.filter.t, colors = dynamicColors)
MEs <- MEList$eigengenes
# Calculate dissimilarity of module eigengenes
MEDiss <- 1-cor(MEs);
# Cluster module eigengenes
METree <- hclust(as.dist(MEDiss), method = "average");
# Plot the result
sizeGrWindow(7, 6)
plot(METree, main = "Clustering of module eigengenes",
     xlab = "", sub = "")

merge with correlation > 0.75

MEDissThres = 0.25
# Plot the cut line into the dendrogram
plot(METree, main = "Clustering of module eigengenes",
     xlab = "", sub = "")
abline(h=MEDissThres, col = "red")

# Call an automatic merging function
merge = mergeCloseModules(lcpm.filter.t, dynamicColors, cutHeight = MEDissThres, verbose = 3)
 mergeCloseModules: Merging modules whose distance is less than 0.25
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 56 module eigengenes in given set.
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 31 module eigengenes in given set.
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 26 module eigengenes in given set.
   Calculating new MEs...
   multiSetMEs: Calculating module MEs.
     Working on set 1 ...
     moduleEigengenes: Calculating 26 module eigengenes in given set.
# The merged module colors
mergedColors = merge$colors
# Eigengenes of the new merged modules:
mergedMEs = merge$newMEs

compare pre and post merge

sizeGrWindow(12, 9)
#pdf(file = "Plots/geneDendro-3.pdf", wi = 9, he = 6)
plotDendroAndColors(geneTree, cbind(dynamicColors, mergedColors),
                    c("Dynamic Tree Cut", "Merged dynamic"),
                    dendroLabels = FALSE, hang = 0.03,
                    addGuide = TRUE, guideHang = 0.05)

#dev.off()
# Rename to moduleColors
moduleColors = mergedColors
# Construct numerical labels corresponding to the colors
colorOrder = c("grey", standardColors(50));
moduleLabels = match(moduleColors, colorOrder)-1;
MEs = mergedMEs 
table(merge$colors)

          black            blue           brown            cyan       darkgreen        darkgrey 
            659            2521            3736             428             423             775 
    darkorange2         darkred   darkslateblue           green     greenyellow          grey60 
            118             730             107            1250             482             825 
          ivory       lightcyan      lightcyan1 lightsteelblue1         magenta          maroon 
           1032            1439            1381             145             912             261 
  mediumpurple3      orangered4           plum2     saddlebrown         salmon4         skyblue 
            170            2120              98             251              87             273 
      steelblue        thistle1 
            246              93 
length(table(merge$colors))
[1] 26
median(table(merge$colors))
[1] 455

Look at modules

Which module is LFY in?

CrLFY1 <- "Ceric.33G031700.2.v2.1" # this is the primary transcript.  There is another but it is expressed at very low levels.

CrLFY2 <- "Ceric.18G076300"

module.assignment <- tibble(geneID=colnames(lcpm.filter.t), module = mergedColors)

module.assignment %>%
  filter(str_detect(geneID, "18G076300|33G031700"))

Interesting: they are in different modules(!). But both are quite large…

module.assignment %>% group_by(module) %>% summarize(n_genes = n()) %>% arrange(n_genes)

Plot eigengenes

Make sure sample info sheet is in the correct order.

rownames(lcpm.filter.t) %>% str_replace_all("\\.", "-") == sample.description$sample
 [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[20] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
sample.eigen <- cbind(sample.description, MEs)
sample.eigen
sample.eigen.l <- sample.eigen %>%
  mutate(gt_tissue=str_c(base_gt, "-", tissue)) %>%
  pivot_longer(starts_with("ME"), names_to = "ME")

sample.eigen.means <- sample.eigen.l %>%
  group_by(gt_tissue, ME) %>%
  summarise(value = mean(value))
`summarise()` has grouped output by 'gt_tissue'. You can override using the `.groups` argument.
sample.eigen.l %>%
  ggplot(aes(x=gt_tissue, y = value)) +
    geom_point(aes(color = tissue)) +
    geom_line(data=sample.eigen.means, group = 1, lwd=.3) + 
  facet_wrap(~ME, ncol=5) +
  theme(axis.text.x = element_text(angle = 90, hjust=1, vjust=.5)) +
  scale_color_brewer(type="qual", palette = "Set3")

A heat map:

MEs.m <- as.matrix(MEs)
heatmap.2(MEs.m, trace="none", cexRow= 0.6, col="bluered")

save(module.assignment, MEs, lcpm.filter, CrLFY1, CrLFY2, file="../output/WGCNA_sporophyteSamples.Rdata")
LS0tCnRpdGxlOiAiMDRfV0dDTkEiCmF1dGhvcjogIkp1bGluIE1hbG9vZiIKZGF0ZTogIjIwMjUtMDItMTYiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KFdHQ05BKQpsaWJyYXJ5KGdwbG90cykKb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKbG9hZCBzYW1wbGUgaW5mbwpgYGB7cn0Kc2FtcGxlLmRlc2NyaXB0aW9uIDwtIHJlYWQuY3N2KCIuLi9pbnB1dC9zYW1wbGUuZGVzY3JpcHRpb24uY3N2IikKYGBgCgoKbG9hZCByZWFkcwoKYGBge3J9CmxjcG0gPC0gcmVhZC5jc3YoIi4uL291dHB1dC9sb2cyY3BtLmNzdi5neiIsIHJvdy5uYW1lcyA9IDEsIGNoZWNrLm5hbWVzID0gRkFMU0UpCmhlYWQobGNwbSkKZGltKGxjcG0pCmBgYAoKRmlsdGVyIGZvciBzcG9yb3BoeXRlIHNhbXBsZXM6CgpgYGB7cn0Kc2FtcGxlLmRlc2NyaXB0aW9uIDwtIHNhbXBsZS5kZXNjcmlwdGlvbiAlPiUgZmlsdGVyKHN0cl9kZXRlY3QodGlzc3VlLCAiUzV8V1lTIikpCmxjcG0gPC0gbGNwbVssc2FtcGxlLmRlc2NyaXB0aW9uJHNhbXBsZV0KYGBgCgoKRmlsdGVyIGZvciBnZW5lcyB3aXRoIHRoZSBoaWdoZXN0IGNvZWZmaWNpZW50IG9mIHZhcmlhdGlvbgoKYGBge3J9CkNWIDwtIGFwcGx5KGxjcG0sIDEsIFwoeCkgYWJzKHNkKHgpL21lYW4oeCkpKQpoaXN0KGxvZzEwKENWKSkKYGBgCmBgYHtyfQpuYW1lcyhDVikgPC0gcm93bmFtZXMobGNwbSkKQ1Zbc3RyX2RldGVjdChuYW1lcyhDViksICIxOEcwNzYzMDB8MzNHMDMxNzAwIildCmBgYAoKYGBge3J9CnF1YW50aWxlKENWLCAwLjIzKQpgYGAKCmBgYHtyfQpsY3BtLmZpbHRlciA8LSBsY3BtW0NWID4gcXVhbnRpbGUoQ1YsIDAuMjMpLF0KZGltKGxjcG0uZmlsdGVyKQpgYGAKCldHQ05BIHdhbnRzIGdlbmVzIGluIGNvbHVtbnMKCmBgYHtyfQpsY3BtLmZpbHRlci50IDwtIHQobGNwbS5maWx0ZXIpCmBgYAoKClNvZnQgdGhyZXNob2xkaW5nCmBgYHtyfQpwb3dlcnMgPC0gYyhjKDE6MTApLCBzZXEoZnJvbSA9IDEyLCB0bz0yMCwgYnk9MikpCnNmdCA8LSBwaWNrU29mdFRocmVzaG9sZChsY3BtLmZpbHRlci50LCBwb3dlclZlY3RvciA9IHBvd2VycywgdmVyYm9zZSA9IDUsbmV0d29ya1R5cGUgPSAic2lnbmVkIGh5YnJpZCIsIGJsb2NrU2l6ZSA9IDE1MDAwKQpgYGAKCmBgYHtyfQpzaXplR3JXaW5kb3coOSwgNSkKcGFyKG1mcm93ID0gYygxLDIpKQpjZXgxIDwtIDAuOQojIFNjYWxlLWZyZWUgdG9wb2xvZ3kgZml0IGluZGV4IGFzIGEgZnVuY3Rpb24gb2YgdGhlIHNvZnQtdGhyZXNob2xkaW5nIHBvd2VyCnBsb3Qoc2Z0JGZpdEluZGljZXNbLDFdLCAtc2lnbihzZnQkZml0SW5kaWNlc1ssM10pKnNmdCRmaXRJbmRpY2VzWywyXSwKICAgICB4bGFiPSJTb2Z0IFRocmVzaG9sZCAocG93ZXIpIix5bGFiPSJTY2FsZSBGcmVlIFRvcG9sb2d5IE1vZGVsIEZpdCxzaWduZWQgUl4yIix0eXBlPSJuIiwKICAgICBtYWluID0gcGFzdGUoIlNjYWxlIGluZGVwZW5kZW5jZSIpKQp0ZXh0KHNmdCRmaXRJbmRpY2VzWywxXSwgLXNpZ24oc2Z0JGZpdEluZGljZXNbLDNdKSpzZnQkZml0SW5kaWNlc1ssMl0sCiAgICAgbGFiZWxzPXBvd2VycyxjZXg9Y2V4MSxjb2w9InJlZCIpCiMgdGhpcyBsaW5lIGNvcnJlc3BvbmRzIHRvIHVzaW5nIGFuIFJeMiBjdXQtb2ZmIG9mIGgKYWJsaW5lKGg9MC45MCxjb2w9InJlZCIpCiMgTWVhbiBjb25uZWN0aXZpdHkgYXMgYSBmdW5jdGlvbiBvZiB0aGUgc29mdC10aHJlc2hvbGRpbmcgcG93ZXIKcGxvdChzZnQkZml0SW5kaWNlc1ssMV0sIHNmdCRmaXRJbmRpY2VzWyw1XSwKICAgICB4bGFiPSJTb2Z0IFRocmVzaG9sZCAocG93ZXIpIix5bGFiPSJNZWFuIENvbm5lY3Rpdml0eSIsIHR5cGU9Im4iLAogICAgIG1haW4gPSBwYXN0ZSgiTWVhbiBjb25uZWN0aXZpdHkiKSkKdGV4dChzZnQkZml0SW5kaWNlc1ssMV0sIHNmdCRmaXRJbmRpY2VzWyw1XSwgbGFiZWxzPXBvd2VycywgY2V4PWNleDEsY29sPSJyZWQiKQpgYGAKY2hvb3NlIDgKCmBgYHtyfQpzb2Z0UG93ZXIgPC0gOAphZGphY2VuY3kgPC0gYWRqYWNlbmN5KGxjcG0uZmlsdGVyLnQsIHBvd2VyID0gc29mdFBvd2VyLCB0eXBlID0gInNpZ25lZCBoeWJyaWQiKQojIFR1cm4gYWRqYWNlbmN5IGludG8gdG9wb2xvZ2ljYWwgb3ZlcmxhcApUT00gPC0gVE9Nc2ltaWxhcml0eShhZGphY2VuY3ksIFRPTVR5cGUgPSAic2lnbmVkIik7CmRpc3NUT00gPC0gMS1UT00KYGBgCgpgYGB7cn0KIyBDYWxsIHRoZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBmdW5jdGlvbgpnZW5lVHJlZSA8LSBoY2x1c3QoYXMuZGlzdChkaXNzVE9NKSwgbWV0aG9kID0gImF2ZXJhZ2UiKQojIFBsb3QgdGhlIHJlc3VsdGluZyBjbHVzdGVyaW5nIHRyZWUgKGRlbmRyb2dyYW0pCnNpemVHcldpbmRvdygxMiw5KQpwbG90KGdlbmVUcmVlLCB4bGFiPSIiLCBzdWI9IiIsIG1haW4gPSAiR2VuZSBjbHVzdGVyaW5nIG9uIFRPTS1iYXNlZCBkaXNzaW1pbGFyaXR5IiwKICAgICBsYWJlbHMgPSBGQUxTRSwgaGFuZyA9IDAuMDQpCmBgYAoKZGVmaW5lIG1vZHVsZXMKCmBgYHtyfQojIFdlIGxpa2UgbGFyZ2UgbW9kdWxlcywgc28gd2Ugc2V0IHRoZSBtaW5pbXVtIG1vZHVsZSBzaXplIHJlbGF0aXZlbHkgaGlnaDoKbWluTW9kdWxlU2l6ZSA8LSAzMDsKIyBNb2R1bGUgaWRlbnRpZmljYXRpb24gdXNpbmcgZHluYW1pYyB0cmVlIGN1dDoKZHluYW1pY01vZHMgPC0gY3V0cmVlRHluYW1pYyhkZW5kcm8gPSBnZW5lVHJlZSwgZGlzdE0gPSBkaXNzVE9NLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZXBTcGxpdCA8LSAyLCBwYW1SZXNwZWN0c0RlbmRybyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbkNsdXN0ZXJTaXplID0gbWluTW9kdWxlU2l6ZSk7CnRhYmxlKGR5bmFtaWNNb2RzKQpgYGAKCmBgYHtyfQojIENvbnZlcnQgbnVtZXJpYyBsYWJlbHMgaW50byBjb2xvcnMKZHluYW1pY0NvbG9ycyA9IGxhYmVsczJjb2xvcnMoZHluYW1pY01vZHMpCnRhYmxlKGR5bmFtaWNDb2xvcnMpCiMgUGxvdCB0aGUgZGVuZHJvZ3JhbSBhbmQgY29sb3JzIHVuZGVybmVhdGgKc2l6ZUdyV2luZG93KDgsNikKcGxvdERlbmRyb0FuZENvbG9ycyhnZW5lVHJlZSwgZHluYW1pY0NvbG9ycywgIkR5bmFtaWMgVHJlZSBDdXQiLAogICAgICAgICAgICAgICAgICAgIGRlbmRyb0xhYmVscyA9IEZBTFNFLCBoYW5nID0gMC4wMywKICAgICAgICAgICAgICAgICAgICBhZGRHdWlkZSA9IFRSVUUsIGd1aWRlSGFuZyA9IDAuMDUsCiAgICAgICAgICAgICAgICAgICAgbWFpbiA9ICJHZW5lIGRlbmRyb2dyYW0gYW5kIG1vZHVsZSBjb2xvcnMiKQpgYGAKCm1lcmdlIHNpbWlsYXIgbW9kdWxlcwoKYGBge3J9CiMgQ2FsY3VsYXRlIGVpZ2VuZ2VuZXMKTUVMaXN0IDwtIG1vZHVsZUVpZ2VuZ2VuZXMobGNwbS5maWx0ZXIudCwgY29sb3JzID0gZHluYW1pY0NvbG9ycykKTUVzIDwtIE1FTGlzdCRlaWdlbmdlbmVzCiMgQ2FsY3VsYXRlIGRpc3NpbWlsYXJpdHkgb2YgbW9kdWxlIGVpZ2VuZ2VuZXMKTUVEaXNzIDwtIDEtY29yKE1Fcyk7CiMgQ2x1c3RlciBtb2R1bGUgZWlnZW5nZW5lcwpNRVRyZWUgPC0gaGNsdXN0KGFzLmRpc3QoTUVEaXNzKSwgbWV0aG9kID0gImF2ZXJhZ2UiKTsKIyBQbG90IHRoZSByZXN1bHQKc2l6ZUdyV2luZG93KDcsIDYpCnBsb3QoTUVUcmVlLCBtYWluID0gIkNsdXN0ZXJpbmcgb2YgbW9kdWxlIGVpZ2VuZ2VuZXMiLAogICAgIHhsYWIgPSAiIiwgc3ViID0gIiIpCmBgYAoKbWVyZ2Ugd2l0aCBjb3JyZWxhdGlvbiA+IDAuNzUKYGBge3J9Ck1FRGlzc1RocmVzID0gMC4yNQojIFBsb3QgdGhlIGN1dCBsaW5lIGludG8gdGhlIGRlbmRyb2dyYW0KcGxvdChNRVRyZWUsIG1haW4gPSAiQ2x1c3RlcmluZyBvZiBtb2R1bGUgZWlnZW5nZW5lcyIsCiAgICAgeGxhYiA9ICIiLCBzdWIgPSAiIikKYWJsaW5lKGg9TUVEaXNzVGhyZXMsIGNvbCA9ICJyZWQiKQojIENhbGwgYW4gYXV0b21hdGljIG1lcmdpbmcgZnVuY3Rpb24KbWVyZ2UgPSBtZXJnZUNsb3NlTW9kdWxlcyhsY3BtLmZpbHRlci50LCBkeW5hbWljQ29sb3JzLCBjdXRIZWlnaHQgPSBNRURpc3NUaHJlcywgdmVyYm9zZSA9IDMpCiMgVGhlIG1lcmdlZCBtb2R1bGUgY29sb3JzCm1lcmdlZENvbG9ycyA9IG1lcmdlJGNvbG9ycwojIEVpZ2VuZ2VuZXMgb2YgdGhlIG5ldyBtZXJnZWQgbW9kdWxlczoKbWVyZ2VkTUVzID0gbWVyZ2UkbmV3TUVzCmBgYAoKY29tcGFyZSBwcmUgYW5kIHBvc3QgbWVyZ2UKYGBge3J9CnNpemVHcldpbmRvdygxMiwgOSkKI3BkZihmaWxlID0gIlBsb3RzL2dlbmVEZW5kcm8tMy5wZGYiLCB3aSA9IDksIGhlID0gNikKcGxvdERlbmRyb0FuZENvbG9ycyhnZW5lVHJlZSwgY2JpbmQoZHluYW1pY0NvbG9ycywgbWVyZ2VkQ29sb3JzKSwKICAgICAgICAgICAgICAgICAgICBjKCJEeW5hbWljIFRyZWUgQ3V0IiwgIk1lcmdlZCBkeW5hbWljIiksCiAgICAgICAgICAgICAgICAgICAgZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLAogICAgICAgICAgICAgICAgICAgIGFkZEd1aWRlID0gVFJVRSwgZ3VpZGVIYW5nID0gMC4wNSkKI2Rldi5vZmYoKQpgYGAKCmBgYHtyfQojIFJlbmFtZSB0byBtb2R1bGVDb2xvcnMKbW9kdWxlQ29sb3JzID0gbWVyZ2VkQ29sb3JzCiMgQ29uc3RydWN0IG51bWVyaWNhbCBsYWJlbHMgY29ycmVzcG9uZGluZyB0byB0aGUgY29sb3JzCmNvbG9yT3JkZXIgPSBjKCJncmV5Iiwgc3RhbmRhcmRDb2xvcnMoNTApKTsKbW9kdWxlTGFiZWxzID0gbWF0Y2gobW9kdWxlQ29sb3JzLCBjb2xvck9yZGVyKS0xOwpNRXMgPSBtZXJnZWRNRXMgCnRhYmxlKG1lcmdlJGNvbG9ycykKbGVuZ3RoKHRhYmxlKG1lcmdlJGNvbG9ycykpCm1lZGlhbih0YWJsZShtZXJnZSRjb2xvcnMpKQoKYGBgCgojIyBMb29rIGF0IG1vZHVsZXMKCldoaWNoIG1vZHVsZSBpcyBMRlkgaW4/CgpgYGB7cn0KQ3JMRlkxIDwtICJDZXJpYy4zM0cwMzE3MDAuMi52Mi4xIiAjIHRoaXMgaXMgdGhlIHByaW1hcnkgdHJhbnNjcmlwdC4gIFRoZXJlIGlzIGFub3RoZXIgYnV0IGl0IGlzIGV4cHJlc3NlZCBhdCB2ZXJ5IGxvdyBsZXZlbHMuCgpDckxGWTIgPC0gIkNlcmljLjE4RzA3NjMwMCIKCm1vZHVsZS5hc3NpZ25tZW50IDwtIHRpYmJsZShnZW5lSUQ9Y29sbmFtZXMobGNwbS5maWx0ZXIudCksIG1vZHVsZSA9IG1lcmdlZENvbG9ycykKCm1vZHVsZS5hc3NpZ25tZW50ICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KGdlbmVJRCwgIjE4RzA3NjMwMHwzM0cwMzE3MDAiKSkKYGBgCkludGVyZXN0aW5nOiB0aGV5IGFyZSBpbiBkaWZmZXJlbnQgbW9kdWxlcyghKS4gIEJ1dCBib3RoIGFyZSBxdWl0ZSBsYXJnZS4uLgoKYGBge3J9Cm1vZHVsZS5hc3NpZ25tZW50ICU+JSBncm91cF9ieShtb2R1bGUpICU+JSBzdW1tYXJpemUobl9nZW5lcyA9IG4oKSkgJT4lIGFycmFuZ2Uobl9nZW5lcykKYGBgCgpQbG90IGVpZ2VuZ2VuZXMKCk1ha2Ugc3VyZSBzYW1wbGUgaW5mbyBzaGVldCBpcyBpbiB0aGUgY29ycmVjdCBvcmRlci4KYGBge3J9CnJvd25hbWVzKGxjcG0uZmlsdGVyLnQpICU+JSBzdHJfcmVwbGFjZV9hbGwoIlxcLiIsICItIikgPT0gc2FtcGxlLmRlc2NyaXB0aW9uJHNhbXBsZQpgYGAKCmBgYHtyfQpzYW1wbGUuZWlnZW4gPC0gY2JpbmQoc2FtcGxlLmRlc2NyaXB0aW9uLCBNRXMpCnNhbXBsZS5laWdlbgpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0Kc2FtcGxlLmVpZ2VuLmwgPC0gc2FtcGxlLmVpZ2VuICU+JQogIG11dGF0ZShndF90aXNzdWU9c3RyX2MoYmFzZV9ndCwgIi0iLCB0aXNzdWUpKSAlPiUKICBwaXZvdF9sb25nZXIoc3RhcnRzX3dpdGgoIk1FIiksIG5hbWVzX3RvID0gIk1FIikKCnNhbXBsZS5laWdlbi5tZWFucyA8LSBzYW1wbGUuZWlnZW4ubCAlPiUKICBncm91cF9ieShndF90aXNzdWUsIE1FKSAlPiUKICBzdW1tYXJpc2UodmFsdWUgPSBtZWFuKHZhbHVlKSkKCnNhbXBsZS5laWdlbi5sICU+JQogIGdncGxvdChhZXMoeD1ndF90aXNzdWUsIHkgPSB2YWx1ZSkpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gdGlzc3VlKSkgKwogICAgZ2VvbV9saW5lKGRhdGE9c2FtcGxlLmVpZ2VuLm1lYW5zLCBncm91cCA9IDEsIGx3ZD0uMykgKyAKICBmYWNldF93cmFwKH5NRSwgbmNvbD01KSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3Q9MSwgdmp1c3Q9LjUpKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIlNldDMiKQpgYGAKQSBoZWF0IG1hcDoKCmBgYHtyLCBmaWcuaGVpZ2h0PTd9Ck1Fcy5tIDwtIGFzLm1hdHJpeChNRXMpCmhlYXRtYXAuMihNRXMubSwgdHJhY2U9Im5vbmUiLCBjZXhSb3c9IDAuNiwgY29sPSJibHVlcmVkIikKYGBgCgoKYGBge3J9CnNhdmUobW9kdWxlLmFzc2lnbm1lbnQsIE1FcywgbGNwbS5maWx0ZXIsIENyTEZZMSwgQ3JMRlkyLCBmaWxlPSIuLi9vdXRwdXQvV0dDTkFfc3Bvcm9waHl0ZVNhbXBsZXMuUmRhdGEiKQpgYGAKCg==